package com.company.dcm.server.tcp;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.company.dcm.controller.OTAController;
import com.company.dcm.entity.*;
import com.company.dcm.mapper.OtaMapper;
import com.company.dcm.server.protocol.enums.DowntimeReasonEnum;
import com.company.dcm.service.*;
import com.company.dcm.utils.BinHexUtil;
import com.company.dcm.utils.ObjectUtils;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;


@Slf4j
@Component
public class TCPService {
    @Autowired
    private Device_otaService deviceOtaService;
    @Autowired
    private OtaMapper otaMapper;
    @Autowired
    private R_ClothSheetService r_clothSheetService;
    @Autowired
    private R_ClothSheetDetailService r_clothSheetDetailService;
    @Autowired
    private R_DeviceInfoService r_deviceInfoService;
    @Autowired
    private R_DevStopAlarmService r_devStopAlarmService;
    @Autowired
    private R_DevStopHisService r_devStopHisService;
    @Autowired
    private MES_CameraService meS_CameraService;
    @Autowired
    private MES_SwitchDetectionService meS_SwitchDetectionService;
    @Autowired
    private MES_ImageReportService meS_ImageReportService;
    @Autowired
    private MES_EquipIpService meS_EquipIpService;


    @Value("${dcm.detection-path}")
    private String basePath;

    public void setRT_DeviceInfo(ChannelHandlerContext ctx,byte[] array) throws IOException {
        R_DeviceInfo rDeviceInfo = new R_DeviceInfo();
        String deviceCode = BinHexUtil.byteArrayToAsciiString(array, 0, 8);
        saveEquipmentSerial(ctx,Arrays.copyOfRange(array, 0, 8));
        String orderCode = BinHexUtil.byteArrayToAsciiString(array, 8, 16);
        int speed = BinHexUtil.byteArrayToInt(array,16,20);
        int circleCount = BinHexUtil.byteArrayToInt(array,20,24);
        int defectCount = BinHexUtil.byteArrayToInt(array,24,28);
        String careTaker = BinHexUtil.byteArrayToAsciiString(array, 28, 30);
        Boolean isAlarmState = array[30] != 0;
        Boolean checkStatus = array[31] != 0;
        Date lastOnlineTime = new Date(BinHexUtil.byteArrayToInt(array,32,36)* 1000L);
        rDeviceInfo.setDowntimeReason(DowntimeReasonEnum.getEnumByValue(array[36]));



        //保存进数据库
        deviceOtaService.saveDevice(deviceCode,ctx);
        deviceOtaService.delectDevice(ctx);

        rDeviceInfo.setDeviceCode(deviceCode);
        rDeviceInfo.setOrderCode(orderCode);
        rDeviceInfo.setSpeed(speed);
        rDeviceInfo.setCircleCount(circleCount);
        rDeviceInfo.setDefectCount(defectCount);
        rDeviceInfo.setCareTaker(careTaker);
        rDeviceInfo.setIsAlarmState(isAlarmState);
        rDeviceInfo.setCheckStatus(checkStatus);
        rDeviceInfo.setLastOnlineTime(lastOnlineTime);

        System.out.println(rDeviceInfo);

        if(!r_deviceInfoService.updateOrInsertDeviceInfo(rDeviceInfo)){
            log.warn("error /deviceInfo {}",rDeviceInfo);
        }
    }

    public void editOrAddRF_ClothSheet(byte[] array){
        R_ClothSheet rClothSheet = getR_ClothSheetFromByteArray(array);
        if(r_clothSheetService.getBaseMapper().update(rClothSheet,new LambdaUpdateWrapper<R_ClothSheet>()
                .eq(R_ClothSheet::getSerialNo,rClothSheet.getSerialNo()))<1){
            if(!r_clothSheetService.save(rClothSheet)){
                log.warn("error /RF_ClothSheet/EditOrAdd {}",rClothSheet);
            }
        }
    }

    public R_ClothSheet getR_ClothSheetFromByteArray(byte[] array) {
        R_ClothSheet rClothSheet = new R_ClothSheet();
        Date checkDate = new Date(BinHexUtil.byteArrayToInt(array,0,4)* 1000L);
        String buFeiCode = BinHexUtil.byteArrayToAsciiString(array, 4, 12);
        String deviceCode = BinHexUtil.byteArrayToAsciiString(array, 12, 20);
        int circleSum = BinHexUtil.byteArrayToInt(array,20,22);
        int defectTurnSum = BinHexUtil.byteArrayToInt(array,22,24);
        String orderCode = BinHexUtil.byteArrayToAsciiString(array, 28, 36);
        String workerCode = BinHexUtil.byteArrayToAsciiString(array, 36, 44);
        String clothInspector = BinHexUtil.byteArrayToAsciiString(array, 44, 46);
        String weaver = BinHexUtil.byteArrayToAsciiString(array, 46, 48);
        Date productDate = new Date(BinHexUtil.byteArrayToInt(array,48,52)* 1000L);
        String serialNo = BinHexUtil.byteArrayToAsciiString(array, 52, 60);

        rClothSheet.setCheckDate(checkDate);
        rClothSheet.setBuFeiCode(buFeiCode);
        rClothSheet.setDeviceCode(deviceCode);
        rClothSheet.setCircleSum(circleSum);
        rClothSheet.setDefectTurnSum(defectTurnSum);
        rClothSheet.setOrderCode(orderCode);
        rClothSheet.setWorkerCode(workerCode);
        rClothSheet.setClothInspector(clothInspector);
        rClothSheet.setWeaver(weaver);
        rClothSheet.setProductDate(productDate);
        rClothSheet.setSerialNo(serialNo);
        return rClothSheet;
    }

    public void addRF_ClothSheetDetail(R_ClothSheetDetail r_clothSheetDetail,byte[] array){
        String pictureName = BinHexUtil.byteArrayToAsciiString(array, 0, 8).replaceFirst("^0+", "") + ".png";//图片名
        String defectName = getDefectNameByCode(BinHexUtil.byteArrayToInt(array,8,9));//瑕疵类别
        String luCoordinate = BinHexUtil.byteArrayToInt(array, 9, 11) +","+ BinHexUtil.byteArrayToInt(array, 11, 13);
        String rdCoordinate = BinHexUtil.byteArrayToInt(array, 13, 15) +","+ BinHexUtil.byteArrayToInt(array, 15, 17);
        Float defectProbability = (float) (array[17] / 100);
        Long id = BinHexUtil.byteArrayToLong(array,18,22);

        r_clothSheetDetail.setSheetId(id);
        r_clothSheetDetail.setDefectName(defectName);
        r_clothSheetDetail.setDefectProbability(defectProbability);
        r_clothSheetDetail.setPictureName(pictureName);
        r_clothSheetDetail.setLuCoordinate(luCoordinate);
        r_clothSheetDetail.setRdCoordinate(rdCoordinate);

        System.out.println(r_clothSheetDetail);

        if(!r_clothSheetDetailService.save(r_clothSheetDetail)){
            log.warn("error /RF_ClothSheetDetail/Add {}",r_clothSheetDetail);
        }
    }

    public R_ClothSheetDetail addRF_ClothSheetDetail(byte[] array){
        R_ClothSheetDetail r_clothSheetDetail = new R_ClothSheetDetail();

        String deviceCode = BinHexUtil.byteArrayToAsciiString(array, 0, 8);
        Date checkDate = BinHexUtil.decodeUnixTimestamp(Arrays.copyOfRange(array, 8, 12), ByteOrder.BIG_ENDIAN);//时间戳UNIX
        Boolean isStop = array[12] != 0;
        Integer deductPoints = (int) array[13];
        int circleNum = BinHexUtil.byteArrayToInt(array,14,18);

//        String serialNo = BinHexUtil.byteArrayToAsciiString(array, 8, 16);


        r_clothSheetDetail.setPicRelativePath(LocalDate.now() + "/" +deviceCode);
        r_clothSheetDetail.setDeviceCode(deviceCode);
        r_clothSheetDetail.setCheckDate(checkDate);
        r_clothSheetDetail.setIsStop(isStop);
        r_clothSheetDetail.setDeductPoints(deductPoints);
        r_clothSheetDetail.setCircleNum(circleNum);

        return r_clothSheetDetail;
    }

    public void equioment_model(ChannelHandlerContext ctx,byte[] array) throws IOException {
        String procedure = BinHexUtil.byteArrayToAsciiString(array,0,3);
        String product_type = BinHexUtil.byteArrayToAsciiString(array,3,6);

        deviceOtaService.saveDevice(product_type,ctx.channel().remoteAddress().toString().split(":")[0]);

        if (procedure.equals("OTA")){
            String ip_port = ctx.channel().remoteAddress().toString();
            ConnectionManager.serialToIpPort.put(ip_port.split(":")[0],ip_port);
            ConnectionManager.ipPortToSerial.put(ip_port,ip_port.split(":")[0]);//将ota的ip作为key值保存，方便后续直接查找
        }

    }

    public void equioment_modelOTA(ChannelHandlerContext ctx,byte[] array) throws IOException {
        String procedure = BinHexUtil.byteArrayToAsciiString(array,0,3);

        if (procedure.equals("OTA")){
            String ip_port = ctx.channel().remoteAddress().toString();
            ConnectionManager.serialToIpPort.put(ip_port.split(":")[0],ip_port);
            ConnectionManager.ipPortToSerial.put(ip_port,ip_port.split(":")[0]);//将ota的ip作为key值保存，方便后续直接查找
        }

    }

    public byte[] getClothInfo(byte[] array){
        String buFeiCode = BinHexUtil.byteArrayToAsciiString(array, 0, 8);
        return new byte[]{
                57,37,16,
                0 ,0 ,0 ,0 ,0 ,0 ,0 ,1,
                0 ,0 ,0 ,0 ,0 ,0 ,0 ,1};
    }

    public byte[] checkUpdate(byte[] array){
        String code = BinHexUtil.byteArrayToAsciiString(array, 0, 8);
        String version = BinHexUtil.byteArrayToAsciiString(array, 8, 16);
        File dir = new File(basePath);
        File[] files = dir.listFiles((dir1, name) -> name.startsWith("FabricDefectDetection_v"));

        if (files == null) {
            return new byte[]{
                    59,37,9,
                    0,0,0,0,0,0,0,0,0};
        }

        List<Version> versions = Arrays.stream(files)
                .map(File::getName)
                .map(name -> name.substring(name.indexOf('v') + 1)) // 提取版本号部分
                .map(Version::new)
                .collect(Collectors.toList());

        if (versions.isEmpty()) {
            return new byte[]{
                    59,37,9,
                    0,0,0,0,0,0,0,0,0};
        }

        Version maxVersion = Collections.max(versions);

        int zeroLength = 0;
        while (zeroLength < version.length() && version.charAt(zeroLength) == '0') {
            zeroLength++;
        }

        if(zeroLength == version.length()){
            return new byte[]{
                    59,37,9,
                    0,0,0,0,0,0,0,0,0};
        }
        Version uploadVersion = new Version(version.substring(zeroLength));
        if(maxVersion.compareTo(uploadVersion)<=0){
            return new byte[]{
                    59,37,9,
                    0,0,0,0,0,0,0,0,0};
        }
        //正常返回值
        byte[] result = new byte[12];
        byte[] maxVersionByte = maxVersion.toByteArray();
        result[0] = 59;
        result[1] = 37;
        result[2] = 9;
        result[3] = 1;
        System.arraycopy(maxVersionByte, 0, result, 4, 8);
        return result;
    }
    @Transactional
    public byte[] getMESSwitchDetection() {
        MES_SwitchDetection mesSwitchDetection = meS_SwitchDetectionService.query().orderByDesc("set_time").last("limit 1").one();
        if(mesSwitchDetection==null||mesSwitchDetection.getOperate()==null) {
            return new byte[]{
                    0x33,0x20, 1
            };
        }
        else if(mesSwitchDetection.getOperate()==1){
            mesSwitchDetection.setUsed(1);
            meS_SwitchDetectionService.updateById(mesSwitchDetection);
            return new byte[]{
                    0x33,0x20, 1
            };
        }
        else {
            mesSwitchDetection.setUsed(1);
            meS_SwitchDetectionService.updateById(mesSwitchDetection);
            return new byte[]{
                    0x33,0x20,0
            };
        }
    }

    public byte[] getMESCamera() {
        MES_Camera camera = meS_CameraService.query().orderByDesc("set_time").last("limit 1").one();
        if(camera==null)
        return new byte[]{
                0x40,0x25,40,
                0,0,0,0,
                0,0,0,0,
                0,0,0,0,
                0,0,0,0,
                0,0,0,0,
                0,0,0,0,
                0,0,0,0,
                0,0,0,0,
                0,0,0,0,
        };
        byte[] bytes = new byte[40];
        bytes[0] = 0x40;
        bytes[1] = 0x25;
        bytes[2] = 40;
        if(camera.getBrightness()==null)
            Arrays.fill(bytes, 3, 7, (byte) 0);
        else {
            byte[] floatBytes = ByteBuffer.allocate(4).putFloat(camera.getBrightness()).array();
            System.arraycopy(floatBytes, 0, bytes, 3, 4);
        }
        if(camera.getContrast()==null)
            Arrays.fill(bytes, 7, 11, (byte) 0);
        else {
            byte[] floatBytes = ByteBuffer.allocate(4).putFloat(camera.getContrast()).array();
            System.arraycopy(floatBytes, 0, bytes, 7, 4);
        }
        if(camera.getSaturation()==null)
            Arrays.fill(bytes, 11, 15, (byte) 0);
        else {
            byte[] floatBytes = ByteBuffer.allocate(4).putFloat(camera.getSaturation()).array();
            System.arraycopy(floatBytes, 0, bytes, 11, 4);
        }
        if(camera.getHue()==null)
            Arrays.fill(bytes, 15, 19, (byte) 0);
        else {
            byte[] floatBytes = ByteBuffer.allocate(4).putFloat(camera.getHue()).array();
            System.arraycopy(floatBytes, 0, bytes, 15, 4);
        }
        if(camera.getSharpness()==null)
            Arrays.fill(bytes, 19, 23, (byte) 0);
        else {
            byte[] floatBytes = ByteBuffer.allocate(4).putFloat(camera.getSharpness()).array();
            System.arraycopy(floatBytes, 0, bytes, 19, 4);
        }
        if(camera.getGamma()==null)
            Arrays.fill(bytes, 23, 27, (byte) 0);
        else {
            byte[] floatBytes = ByteBuffer.allocate(4).putFloat(camera.getGamma()).array();
            System.arraycopy(floatBytes, 0, bytes, 23, 4);
        }
        if(camera.getWhiteBalance()==null)
            Arrays.fill(bytes, 27, 31, (byte) 0);
        else {
            byte[] floatBytes = ByteBuffer.allocate(4).putFloat(camera.getWhiteBalance()).array();
            System.arraycopy(floatBytes, 0, bytes, 27, 4);
        }
        if(camera.getBacklightContrast()==null)
            Arrays.fill(bytes, 31, 35, (byte) 0);
        else {
            byte[] floatBytes = ByteBuffer.allocate(4).putFloat(camera.getBacklightContrast()).array();
            System.arraycopy(floatBytes, 0, bytes, 31, 4);
        }
        if(camera.getExposure()==null)
            Arrays.fill(bytes, 35, 39, (byte) 0);
        else {
            byte[] floatBytes = ByteBuffer.allocate(4).putFloat(camera.getExposure()).array();
            System.arraycopy(floatBytes, 0, bytes, 35, 4);
        }
        return bytes;
    }
    public byte[] getMESImageReport() {
        byte[] bytes = new byte[12];
        bytes[0] = 0x45;
        bytes[1] = 0x25;
        bytes[2] = 9;
        MES_ImageReport imageReport = meS_ImageReportService.query().orderByDesc("set_time").last("limit 1").one();
        if(imageReport==null){
            Arrays.fill(bytes, 3, 12, (byte) 0);
            return bytes;
        }
        if(imageReport.getOperate()==null)
            Arrays.fill(bytes, 3, 4, (byte) 0);
        else {
            byte lowByte = (byte) (imageReport.getOperate() & 0xFF); // 提取低位字节
            bytes[3]=lowByte;
        }
        if(imageReport.getDuration()==null)
            Arrays.fill(bytes, 4, 8, (byte) 0);
        else {
            // 提取 Long 类型的低四个字节
            int lowFourBytes = (int) (imageReport.getDuration() & 0xFFFFFFFFL); // 提取低 32 位并转换为 int 类型

            // 将低四个字节赋值给字节数组的 5 到 8 位
            bytes[4] = (byte) (lowFourBytes >> 24); // 获取最高字节
            bytes[5] = (byte) (lowFourBytes >> 16); // 获取第二字节
            bytes[6] = (byte) (lowFourBytes >> 8);  // 获取第三字节
            bytes[7] = (byte) (lowFourBytes);       // 获取最低字节
        }
        if(imageReport.getGap()==null)
            Arrays.fill(bytes, 8, 12, (byte) 0);
        else {
            // 提取 Long 类型的低四个字节
            int lowFourBytes = (int) (imageReport.getGap() & 0xFFFFFFFFL); // 提取低 32 位并转换为 int 类型

            // 将低四个字节赋值给字节数组的 9 到 12 位
            bytes[8] = (byte) (lowFourBytes >> 24); // 获取最高字节
            bytes[9] = (byte) (lowFourBytes >> 16); // 获取第二字节
            bytes[10] = (byte) (lowFourBytes >> 8);  // 获取第三字节
            bytes[11] = (byte) (lowFourBytes);       // 获取最低字节
        }
        return bytes;
    }
    private String getDefectNameByCode(int code){
        switch (code){
            case 0:return "点疵";
            case 1:return "线疵";
            case 2:return "开幅线";
            case 3:return "褶皱";
        }
        return "未知";
    }

    public void saveEquipmentSerial(ChannelHandlerContext ctx, byte[] values) {
        // 检查数组长度是否足够
        System.out.println(Arrays.toString(values));
        if (values.length >= 8) {
            byte[] subArray = new byte[8];
            System.arraycopy(values, 0, subArray, 0, 8);
//            String serial = new String(subArray);
            String serial = BinHexUtil.byteArrayToAsciiString(subArray, 0, 8);
            String ip_port = ctx.channel().remoteAddress().toString();
            ConnectionManager.serialToIpPort.put(serial,ip_port);
            ConnectionManager.ipPortToSerial.put(ip_port,serial);
            // 去掉开头的 '/'
            String trimmedInput = ip_port.substring(1);
            // 按 ':' 分割字符串
            String[] parts = trimmedInput.split(":");
            if (parts.length == 2) {
                String ipAddress = parts[0];
                String port = parts[1];
                System.out.println("IP 地址: " + ipAddress);
                System.out.println("端口号: " + port);
                MES_EquipIp equipmentNo = meS_EquipIpService.query().eq("equipment_no", serial).one();
                if(equipmentNo==null){
                    equipmentNo = new MES_EquipIp();
                    equipmentNo.setEquipment_no(serial);
                    equipmentNo.setIp(ipAddress);
                    equipmentNo.setPort(port);
                    equipmentNo.setCreate_time(new Date());
                    meS_EquipIpService.save(equipmentNo);
                }else {
                    equipmentNo.setIp(ipAddress);
                    equipmentNo.setPort(port);
                    equipmentNo.setUpdate_time(new Date());
                    meS_EquipIpService.updateById(equipmentNo);
                }
            } else {
                System.err.println("输入格式不正确");
            }
        } else {
            throw new RuntimeException("设备号长度不够");
        }
    }

    public byte[] noticeOtaUpdate(String SoftwareVersion,String HardwareVersion) {

        byte[] bytes = new byte[17];

        bytes[0] = 0x07;//变量点id
        bytes[1] = 0x25;//变量点id
        bytes[2] = 0x0E;//数据长度
        bytes[3] = 0x01;//是否更新


//        if (isUpdate){//是否更新
//            bytes[5] = 0x01;
//        }else {
//            bytes[5] = 0x00;
//        }

        // 分割软件版本号为主版本和次版本
        String[] SoftwareParts = SoftwareVersion.split("\\.");
        if (SoftwareParts.length != 3) {
            throw new IllegalArgumentException("版本号格式不正确，应为x.x.x");
        }

        for (int i = 0; i < 3; i++) {
            if (SoftwareParts[i].length() < 2){
                SoftwareParts[i] = '0' + SoftwareParts[i];
            }
        }

        // 分割硬件版本号为主版本和次版本
        String[] HardwareParts = HardwareVersion.split("\\.");
        if (HardwareParts.length != 2) {
            throw new IllegalArgumentException("版本号格式不正确，应为x.x");
        }

        for (int i = 0; i < 2; i++) {
            if (HardwareParts[i].length() < 2){
                HardwareParts[i] = '0' + HardwareParts[i];
            }
        }

        // 构造13字符的版本字符串
        String versionStr = String.format("%s.%s%s.%s.%s", HardwareParts[0], HardwareParts[1],SoftwareParts[0],SoftwareParts[1],SoftwareParts[2]);

//         确认字符串长度正确
        if (versionStr.length() != 13) {
            throw new RuntimeException("生成的版本字符串长度不正确: " + versionStr);
        }


        System.arraycopy(versionStr.getBytes(StandardCharsets.US_ASCII), 0, bytes,4, 13);


        return bytes;
    }

    public static class Version implements Comparable<Version> {
        private final int[] numbers;

        public Version(String version) {
            this.numbers = Arrays.stream(version.split("\\."))
                    .mapToInt(Integer::parseInt)
                    .toArray();
        }

        public byte[] toByteArray(){

            byte[] result = new byte[8];
            for (int i = 0; i < 8; i++) {
                result[i]='0';
            }
            int i = 8 - this.numbers.length*2 + 1;
            int j = 0;
            while(i<8){
                result[i++] = (byte) (this.numbers[j++] + 48);
                if(i<8){
                    result[i++] = 46;
                }
            }
            return result;
        }

        @Override
        public int compareTo(Version other) {
            for (int i = 0; i < Math.max(this.numbers.length, other.numbers.length); i++) {
                int num1 = i < this.numbers.length ? this.numbers[i] : 0;
                int num2 = i < other.numbers.length ? other.numbers[i] : 0;
                if (num1 != num2) {
                    return Integer.compare(num1, num2);
                }
            }
            return 0;
        }

        @Override
        public String toString() {
            return Arrays.stream(numbers).mapToObj(String::valueOf).collect(Collectors.joining("."));
        }
    }

}
